package rapier.sso.core.config.redis;

import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.connection.ReturnType;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.stereotype.Component;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;

/**
* Title:  <br>
* Desc:  REDIS工具类<br>
* Date: 2019年3月26日 <br>
* Company: 联通（辽宁）产业互联网有限公司 <br>
* 
* @author zxd
* @version 1.0.0.0
*/ 
@Component
public class RedisClient {
	@Autowired
	private RedisTemplate redisTemplate;

	/**  
	* Desc: 写入缓存
	* @param key
	* @param value
	* @return
	* @author zxd
	*/
	public boolean set(final String key, Object value) {
		boolean result = false;
		try {
			ValueOperations<Serializable, Object> operations = redisTemplate
					.opsForValue();
			operations.set(key, value);
			result = true;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	/**  
	* Desc: 写入缓存设置时效时间
	* @param key
	* @param value
	* @param expireTime
	* @param timeUnit
	* @return
	* @author zxd
	*/
	public boolean set(final String key, Object value, Long expireTime,
			TimeUnit timeUnit) {
		boolean result = false;
		try {
			ValueOperations<Serializable, Object> operations = redisTemplate
					.opsForValue();
			operations.set(key, value);
			redisTemplate.expire(key, expireTime, timeUnit);
			result = true;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	/**  
	* Desc: 批量删除对应的value
	* @param keys
	* @author zxd
	*/
	public void remove(final String... keys) {
		for (String key : keys) {
			remove(key);
		}
	}

	/**  
	* Desc: 批量删除key
	* @param pattern
	* @author zxd
	*/
	public void removePattern(final String pattern) {
		Set<Serializable> keys = redisTemplate.keys(pattern);
		if (keys.size() > 0) {
			redisTemplate.delete(keys);
		}
	}

	/**  
	* Desc: 删除对应的value
	* @param key
	* @author zxd
	*/
	public void remove(final String key) {
		if (exists(key)) {
			redisTemplate.delete(key);
		}
	}

	/**  
	* Desc: 判断缓存中是否有对应的value
	* @param key
	* @return
	* @author zxd
	*/
	public boolean exists(final String key) {
		if(StringUtils.isBlank(key)) {
			return false;
		}
		return redisTemplate.hasKey(key);
	}

	/**  
	* Desc: 读取缓存
	* @param key
	* @return
	* @author zxd
	*/
	public Object get(final String key) {
		Object result = null;
		ValueOperations<Serializable, Object> operations = redisTemplate
				.opsForValue();
		result = operations.get(key);

		return result;
	}
	/**
	 * @Desc: getBykeys
	 * @Author  zxd
	 * @Date   2020-01-31 16:18
	 * @Param keys
	 * @return java.lang.Object
	 * @Exception
	 */
	public Set getBykeys(final String keys) {
		Object result = null;
		Set keys1 = redisTemplate.keys(keys);
		return keys1;
	}
	/**  
	* Desc: 获得当前KEY的自增值
	* @param key
	* @return
	* @author zxd
	*/
	public Long getIncr(String key) {
        RedisAtomicLong entityIdCounter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory());
        Long increment = entityIdCounter.incrementAndGet();
        return increment;
    }

	/**  
	* Desc: 哈希添加
	* @param key
	* @param hashKey
	* @param value
	* @author zxd
	*/
	public void hmSet(String key, Object hashKey, Object value) {
		HashOperations<String, Object, Object> hash = redisTemplate
				.opsForHash();
		hash.put(key, hashKey, value);
	}
	
	/**  
	* Desc: 哈希获取数据
	* @param key
	* @param hashKey
	* @return
	* @author zxd
	*/
	public Object hmGet(String key, Object hashKey) {
		HashOperations<String, Object, Object> hash = redisTemplate
				.opsForHash();
		return hash.get(key, hashKey);
	}

	/**  
	* Desc: 列表添加
	* @param k
	* @param v
	* @author zxd
	*/
	public void lPush(String k, Object v) {
		ListOperations<String, Object> list = redisTemplate.opsForList();
		list.rightPush(k, v);
	}

	/**  
	* Desc: 列表获取
	* @param k
	* @param l
	* @param l1
	* @return
	* @author zxd
	*/
	public List<Object> lRange(String k, long l, long l1) {
		ListOperations<String, Object> list = redisTemplate.opsForList();
		return list.range(k, l, l1);
	}

	/**  
	* Desc: SET集合添加
	* @param key
	* @param value
	* @author zxd
	*/
	public void opsForSetAdd(String key, Object value) {
		SetOperations<String, Object> set = redisTemplate.opsForSet();
		set.add(key, value);
	}

	/**  
	* Desc: SET集合获取
	* @param key
	* @return
	* @author zxd
	*/
	public Set<Object> opsForSet(String key) {
		SetOperations<String, Object> set = redisTemplate.opsForSet();
		return set.members(key);
	}

	/**  
	* Desc: 有序集合添加
	* @param key
	* @param value
	* @param scoure
	* @author zxd
	*/
	public void zAdd(String key, Object value, double scoure) {
		ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
		zset.add(key, value, scoure);
	}
	
	/**  
	* Desc: 有序集合获取
	* @param key
	* @param scoure
	* @param scoure1
	* @return
	* @author zxd
	*/
	public Set<Object> rangeByScore(String key, double scoure, double scoure1) {
		ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
		return zset.rangeByScore(key, scoure, scoure1);
	}

	/**  
	* Desc: 全局锁
	* @param key
	* @param value
	* @param exSeconds
	* @return
	* @author zxd
	*/
	public Boolean lock(final String key, final String value,
			final int exSeconds) {
		return (Boolean) redisTemplate.execute(new RedisCallback<Boolean>() {
			@Override
			public Boolean doInRedis(RedisConnection redisConnection)
					throws DataAccessException {
				StringRedisSerializer serializer = new StringRedisSerializer();
				redisConnection.set(serializer.serialize(key),
						serializer.serialize(value),
						Expiration.seconds(exSeconds),
						RedisStringCommands.SetOption.SET_IF_ABSENT);
				Long keyExist = redisConnection.eval(
						serializer
								.serialize("if redis.call('get', KEYS[1]) == ARGV[1] then return 1 else return 0 end"),
						ReturnType.fromJavaType(Long.class), 1, serializer
								.serialize(key), serializer.serialize(value));
//				String s = serializer.deserialize(redisConnection.get(serializer.serialize(key)));
				boolean result = (keyExist == 1l);
				return result;
			}
		});
	}

	/**  
	* Desc: 解锁
	* @param key
	* @param value
	* @return
	* @author zxd
	*/
	public Boolean unlock(String key, String value) {
		StringRedisSerializer serializer = new StringRedisSerializer();
		DefaultRedisScript redisScript = new DefaultRedisScript();
		redisScript
				.setScriptText("if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end");
		redisScript.setResultType(Long.class);
		Long result = (Long) redisTemplate.execute(redisScript, serializer,
				serializer, Collections.singletonList(key), value);
		
		return result == 1l;
	}
	/**  
	* Desc: 发布消息
	* @param argChannel
	* @param argMessage
	* @author zxd
	*/
	public void convertAndSend(String argChannel, Object argMessage)
	{
		redisTemplate.convertAndSend(argChannel, argMessage);
	}
}